#include "./../Headers/m_thread.h"

#ifdef ALLOW_DEBUGS
#define DEBUG_SEM
#endif

namespace MThread {

	extern "C" void tss_cleanup_implemented(void) {};

	Semaphore::Semaphore( const usint &simult ):
		n(simult), m(){
#ifdef DEBUG_SEM
		debug_function_info( "Constructor", "Semaphore", &(*this), sizeof(*this) );
#endif
	};
	Semaphore::Semaphore( const Semaphore &original ):
		n(original.n), m(){
#ifdef DEBUG_SEM
		debug_function_info( "Constructor (copy)", "Semaphore", &(*this), sizeof(*this) );
#endif
	};
	Semaphore::~Semaphore(){
#ifdef DEBUG_SEM
		debug_function_info( "Destructor", "Semaphore", &(*this), sizeof(*this) );
#endif
	};

	void Semaphore::Wait() {
		while(1) {
			m.lock();
			
			if( n > 0 ) {
				--n;
				m.unlock();
				return;
			}
			m.unlock();
			thread::yield();
		}
	}

	void Semaphore::Signal() {
		m.lock();
		++n;
		m.unlock();
	}

	Semaphore *sem_cout = new Semaphore();

	FunctorHolder::FunctorHolder( Functor *f ):
		func(f){
#ifdef DEBUG_FUNCTOR
		debug_function_info( "Constructor", "FunctorHolder", &(*this), sizeof(*this) );
#endif
	};
	FunctorHolder::FunctorHolder( const FunctorHolder &original ):
		func(original.func){
#ifdef DEBUG_FUNCTOR
		debug_function_info( "Constructor (copy)", "FunctorHolder", &(*this), sizeof(*this) );
#endif
	};
	FunctorHolder::~FunctorHolder(){
#ifdef DEBUG_FUNCTOR
		debug_function_info( "Destructor", "FunctorHolder", &(*this), sizeof(*this) );
#endif
	};

	void ThreadLauncher( const deque< Functor* > &routines, const uint &simult_thread ){
		deque< thread* > t_list;

		for( usint i = 0; i < routines.size(); ++i ){ // launching
			if( t_list.size() >= simult_thread ){ // avoids too many threads running
				t_list.front()->join();
#ifdef DEBUG_M_THREAD
				sem_cout->Wait();
				cout << "join: " << i-t_list.size()-1 << endl;
				sem_cout->Signal();
#endif
				t_list.pop_front();
			}

#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "launching: " << i << endl;
			sem_cout->Signal();
#endif
			// creates and stores the new thread
			t_list.push_back(
				new thread(
					FunctorHolder( routines[i] )
				)
			);

#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "launched: " << i << endl;
			sem_cout->Signal();
#endif
		}

		while( t_list.size() > 0 ){ // joining
			t_list.front()->join();
			t_list.pop_front();
#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "join: " << routines.size()-t_list.size()-1 << endl;
			sem_cout->Signal();
#endif
		}

#ifdef DEBUG_M_THREAD
		cout << "Single!" << endl;
#endif

	};

	void ThreadLauncher( Functor* routine, const uint &max_thread, const uint &simult_thread ){

		deque< thread* > t_list;

		for( usint i = 0; i < max_thread; ++i ){ // launching
			if( t_list.size() >= simult_thread ){ // avoids too many threads running
				t_list.front()->join();
#ifdef DEBUG_M_THREAD
				sem_cout->Wait();
				cout << "join: " << i-t_list.size()-1 << endl;
				sem_cout->Signal();
#endif
				t_list.pop_front();
			}

#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "launching: " << i << endl;
			sem_cout->Signal();
#endif

			// creates and stores the new thread
			t_list.push_back(
				new thread(
					FunctorHolder( routine )
				)
			);

#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "launched: " << i << endl;
			sem_cout->Signal();
#endif
		}

		while( t_list.size() > 0 ){ // joining
			t_list.front()->join();
			t_list.pop_front();
#ifdef DEBUG_M_THREAD
			sem_cout->Wait();
			cout << "join: " << max_thread-t_list.size()-1 << endl;
			sem_cout->Signal();
#endif
		}

#ifdef DEBUG_M_THREAD
		cout << "Single!" << endl;
#endif

	};
};
